/**************************************************************************
 *
 * Copyright 2014 MaeckySoft Ltd., Bludenz, Austria.
 * All Rights Reserved.
 *
 **************************************************************************/

#include <png.h>
#include "MPNG.h"
#include <MTools.h>


MPNG::MPNG(int iDebug = 0) : M3DBase(iDebug)
{
	if (iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;

    init(0,0,MPNG_RGBA, (unsigned char*) 0);
}
MPNG::MPNG(int iWidth, int iHeight, int iDebug = 0) : M3DBase(iDebug)
{
	if (iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;

    init(iWidth,iHeight,MPNG_RGBA, (unsigned char*) 0);
}

MPNG::MPNG(int iWidth, int iHeight, int iFormat, int iDebug = 0) : M3DBase(iDebug)
{
	if (iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;

    init(iWidth,iHeight,iFormat, (unsigned char*)0);
}

MPNG::MPNG(int iWidth, int iHeight, int iFormat, unsigned char* szImageBuffer, int iDebug = 0) : M3DBase(iDebug)
{
	if (iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;

    init(iWidth,iHeight,iFormat,szImageBuffer);
}


void MPNG::printMethodInfo(std::string method)
{
	if (m_iDebug == MDBG_ALL) std::cout << "(" << __FILE__ << ") <DBG_ALL> " << method << " called." << std::endl;
}


void MPNG::init(int iWidth, int iHeight, int iFormat, unsigned char* szImageBuffer)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    this->setSize(iWidth,iHeight);
    this->setFormat(iFormat);
    this->setBuffer(szImageBuffer);
}

unsigned char* MPNG::getBuffer()
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    return m_sBuffer;
}

int MPNG::getFormat()
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    return m_iFormat;
}

int MPNG::getWidth()
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    return m_iWidth;
}
int MPNG::getHeight()
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    return m_iHeight;
}

int MPNG::writeFile(std::string sFilename)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    if (m_iHeight == 0 || m_iWidth == 0) return 1;
    if (this->m_sBuffer==(unsigned char*) 0) return 2;

    FILE *fp=(FILE *)0;
    int iCode =0;
   	png_structp png_ptr;
	png_infop info_ptr;
	png_bytep row;
	char *sTitle=NULL;

#ifdef _WIN32
	fopen_s(&fp,sFilename.c_str(), "wb");
#else  //GCC Linux
	fp = fopen(sFilename.c_str(), "wb");
#endif

	if (fp == NULL) {
		std::cerr << "(" << __FILE__ << ") <Error> Could not open file " << sFilename << " for writing." << std::endl;
		iCode = 1;
		return iCode;
	}
    if (m_iDebug >= MDBG_VERBOSE) std::cout << "( " << __FILE__ << ") <DBG_VERBOSE> open PNG file." << std::endl;

    // Initialize write structure
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		std::cerr << "(" << __FILE__ << ")  <Error> Could not allocate write struct." << std::endl;
		iCode = 1;
		return iCode;
	}
    if (m_iDebug >= MDBG_VERBOSE) std:: cout << "(" <<__FILE__ << ") <DBG_VERBOSE> init PNG structure." << std::endl;

// Initialize info structure
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		std::cerr << "(" << __FILE__ << ") <Error> Could not allocate info struct." << std::endl;
		iCode = 1;
		return iCode;
	}
    if (m_iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> setup PNG exception handler." << std::endl;

	// Setup Exception handling
	if (setjmp(png_jmpbuf(png_ptr))) {
		std::cerr << "(" << __FILE__ << ") <Error> Error during png creation." << std::endl;
		iCode = 1;
		return iCode;
	}
	png_init_io(png_ptr, fp);
    if (m_iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> write PNG file header." << std::endl;

// Write header (8 bit colour depth)
	png_set_IHDR(png_ptr, info_ptr, m_iWidth, m_iHeight,
			8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
			PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

    if (sTitle != NULL) {
		png_text title_text;
		title_text.compression = PNG_TEXT_COMPRESSION_NONE;
		title_text.key = '\0';
		title_text.text = sTitle;
		png_set_text(png_ptr, info_ptr, &title_text, 1);
	}
    if (m_iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> write PNG file info." << std::endl;

    png_write_info(png_ptr, info_ptr);
    int iPixelsize = this->getFormat();

	// Allocate memory for one row (3 bytes per pixel - RGB)
	row = (png_bytep) malloc(m_iWidth * iPixelsize *sizeof(png_byte));


// Write sImage data

	int x;
	int y;

    if (m_iDebug >= MDBG_VERBOSE) std::cout << "(" __FILE__ << ") <DBG_VERBOSE> write PNG file data." << std::endl;

    for (y=0 ; y<m_iHeight ; y++) {
        for (x=0; x < m_iWidth ; x++)
        {
            memcpy(row, &m_sBuffer[y*m_iWidth*iPixelsize], (m_iWidth*iPixelsize*sizeof(png_byte)));
        }
        png_write_row(png_ptr, row);
		std::cout << "\rPNG compress/write " << 100 * (float)y/ (float)m_iHeight  << "%     ";
	}
	std::cout << "\rPNG compress/write 100%     " << std::endl << std::endl;

    if (fp != NULL) fclose(fp);
	if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
	if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
	if (row != NULL) free(row);

    return iCode;
}

void MPNG::setBuffer(unsigned char *sBuffer)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    m_sBuffer = sBuffer;
}

void MPNG::setSize(int iWidth, int iHeight)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    m_iWidth=iWidth;
    m_iHeight = iHeight;
}

void MPNG::setFormat(int iFormat)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    m_iFormat = iFormat;
}

MPNG::~MPNG()
{
	if (m_iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;

    //dtor
}
